home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1994-12-08 | 46.9 KB | 1,237 lines | [ TEXT/R*ch]
C.S.M.P. Digest Sat, 17 Oct 92 Volume 1 : Issue 186 Today's Topics: Release 0.5 of Caml Light, an implementation of the ML language OpenSelection Code (pascal source) EditText items larger than 255 bytes? Adding menus to existing applications (a la Thunder) Game Techniques (was: NON-QUICKDRAW GAMES) The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly. The digest is a collection of article threads from the internet newsgroup comp.sys.mac.programmer. It is designed for people who read c.s.m.p. semi- regularly and want an archive of the discussions. If you don't know what a newsgroup is, you probably don't have access to it. Ask your systems administrator(s) for details. (This means you can't post questions to the digest.) Each issue of the digest contains one or more sets of articles (called threads), with each set corresponding to a 'discussion' of a particular subject. The articles are not edited; all articles included in this digest are in their original posted form (as received by our news server at cs.uoregon.edu). Article threads are not added to the digest until the last article added to the thread is at least one month old (this is to ensure that the thread is dead before adding it to the digest). Article threads that consist of only one message are generally not included in the digest. The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu [128.223.8.8] in the directory /pub/mac/csmp-digest. Be sure to read the file /pub/mac/csmp-digest/README before downloading any files. The most recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the directory /info-mac/digest/csmp. If you don't have ftp capability, the sumex archive has a mail server; send a message with the text '$MACarch help' (no quotes) to LISTSERV@ricevm1.rice.edu for more information. The digest is also available via email. Just send a note saying that you want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will automatically receive each new issue as it is created. Sorry, back issues are not available through the mailing list. Send administrative mail to mkelly@cs.uoregon.edu. ------------------------------------------------------- From: Xavier.Leroy@inria.fr (Xavier Leroy) Subject: Release 0.5 of Caml Light, an implementation of the ML language Date: 11 Sep 92 15:58:05 GMT Organization: INRIA Rocquencourt, France Caml Light is a small, portable implementation of the ML language, that runs on most Unix machines. It has also been ported to the Macintosh and to the IBM PC. Caml Light implements the Caml language, a functional language from the ML family. Caml is quite close to Standard ML, though not strictly conformant. There are some slight differences in syntax and semantics, and major differences in the module system (these changes were required to support separate compilation). The Caml Light implementation is mature and robust enough to be fully bootstrapped, yet the whole system is quite small (about 100K for the runtime system, and another 100K of bytecode for the compiler; 1.5 megabyte of memory is enough to recompile the whole system), and easy to port to almost any 32-bit platform. Caml Light comes in two flavors: a classical, interactive, toplevel-based system; and a standalone, batch-oriented compiler that produces standalone programs, in the spirit of the Unix cc compiler. The version 0.5 of the Caml Light system has been released, and is available by anonymous FTP from: host: nuri.inria.fr (128.93.1.26) directory: lang/caml-light files: cl5unix.tar.Z Complete source code for Unix machines, plus a bootstrap compiler. cl5docps.Z Compressed Postscript for the Caml Light documentation (290 pages). cl5docdvi.tar.Z Compressed DVI (with Postscript inserts) for the Caml Light documentation (290 pages). cl5macbin.sea.hqx Binaries for the Macintosh version. cl5pc386bin.zip Binaries for the 80386 PC version. cl5pc86bin.zip Binaries for the 8086 PC version. cl5macsrc.sea.hqx Source code for the Macintosh version. cl5pc386src.zip Source code for the 80386 PC version. cl5pc86src.zip Source code for the 8086 PC version. The Unix version should work on any modern workstation. We have tested it on Sun 3 and 4, DecStations, HP 9000/700 and 9000/350, IBM RS/6000, SGI Indigo, Sony News, Next cubes, and some more exotic machines. The Macintosh version is now a standalone Macintosh application, and no longer requires the Macintosh Programmer's Workshop. (Well, at least for the toplevel environment; the batch compilers still run under MPW.) The application provides some graphics primitives. The PC version still comes in two flavors, one that run on any PC, but is severely limited by the 640K barrier, and one that run on 80386 or 80486 PC's in 32 bit protected mode to circumvent these limitations. Both versions now provide the same graphics primitives as the Macintosh version. Ports to OS/2 and to the Amiga are in progress. MAIN IMPROVEMENTS IN THIS RELEASE: * Much more documentation. There's now a reference manual for the language, and a complete description of the commands. The tutorial has been extended, too. * Language extensions: streams and stream parsers, a la Mauny/de Rauglaudre. * Extensions to the environment: a general mechanism to link Caml Light code with C code. An X-windows toolkit based on this mechanism should be released soon. A new command, camllibr, to build libraries of Caml Light object files. A new command, camlmktop, to construct custom toplevel systems. * Bug fixes: many of them. * Implementation changes: a new garbage collector. The major collector is now incremental, meaning that garbage collection is even less disruptive than before. Many minor optimizations in the compiler back-end. The linker has been completely reworked, and is now clever enough to avoid linking in useless code. * For the PC versions: line editing is supported. The 80386 port is VCPI-compliant. FEEDBACK: Please send your questions and comments to the Caml mailing list: caml-list@margaux.inria.fr To subscribe or unsubscribe to the list, mail to caml-list-request@margaux.inria.fr. - - Xavier Leroy %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % The Caml Light racing team, projet Formel, INRIA Rocquencourt. % % "Low tar, Caml taste". % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --------------------------- From: mxmora@unix.sri.com (Matthew Xavier Mora) Subject: OpenSelection Code (pascal source) Date: 11 Sep 92 17:11:08 GMT Organization: SRI International Here's the code to have the finder open a control panel. Just pass the control panel's fsspec to the openselection function and it should open it. Actually I think it will open any valid fsspec but I wouldn't bet on it. I haven't stressed this code so there are probably some bugs in it. Matt - -------------------------------------------- { OpenSelection.p ported by Matthew Xavier Mora} { ported from C.K. Han's openselection.c code } { 09-11-92 } unit openSelection; interface uses AppleTalk, PPCToolBox, Processes, EPPC, Notification, AppleEvents, {, Errors, Events, Memory, Resources, SegLoad, } (* Group 3 *) Aliases {, thermometer}; function OpenSelection (var theFileToOpen: FSSpec): OSErr; implementation const kFinderType = 'FNDR'; kSysCreator = 'MACS'; kAEOpenSelection = 'sope'; aeSelectionKeyword = 'fsel'; { This runs through the process list looking for the indicated application } function FindAProcess (typeToFind, creatorToFind: Ostype; var processSN:ProcessSerialNumber; var infoRecToFill: ProcessInfoRec): OSErr; var tempPSN: ProcessSerialNumber; myErr: OSErr; begin myErr := noErr; tempPSN.lowLongOfPSN := kNoProcess; processSN.lowLongOfPSN := kNoProcess; processSN.highLongOfPSN := kNoProcess; repeat begin myErr := GetNextProcess(processSN); if (myErr = noErr) then myErr := GetProcessInformation(processSN, infoRecToFill); end; until ((infoRecToFill.processSignature = creatorToFind) or (infoRecToFill.processType = Longint(typeToFind)) or (myErr <> noErr)); FindAProcess := myErr; end; function OpenSelection (var theFileToOpen: FSSpec): OSErr; var aeEvent, aeReply: AppleEvent; aeDirDesc, listElem: AEDesc; interactErr: OSErr; dirSpec, procSpec: FSSpec; fileList: AEDesc; myReply: StandardFileReply; myErr: OSErr; process: ProcessSerialNumber; DirAlias, FileAlias: AliasHandle; infoRec: ProcessInfoRec; processName: Str31; fullPath, appName: Str255; myAddressDesc: AEDesc; begin interactErr := noErr; if (true) then begin infoRec.processInfoLength := sizeof(ProcessInfoRec); infoRec.processName := @processName; infoRec.processAppSpec := @procSpec; myErr := FindAProcess(kFinderType, kSysCreator, process, infoRec); if (myErr = noErr) then myErr := AECreateDesc(typeProcessSerialNumber, @process, sizeof(process), myAddressDesc); if (myErr = noErr) then begin { Create the FinderEvent } myErr := AECreateAppleEvent(kFinderType, kAEOpenSelection, myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID, aeEvent); { If you want to keep talking to this machine, you can keep this } { address desc around } myErr := AEDisposeDesc(myAddressDesc); if (myErr = noErr) then begin { Now we build all the bits of an OpenSelection event. } { Basically, we need to create an alias for the item to open,} { and an alias to the parent } { folder (directory) of that item. } { We can also pass a list of files if we want. } { You'll notice that for opening a finder window, the file and} { directory alias both point at the } { folder itself } { make a spec for the parent folder } myErr := FSMakeFSSpec(theFileToOpen.vRefNum, theFileToOpen.parID, '', dirSpec); myErr := NewAlias(nil, dirSpec, DirAlias); { Create alias for file } { if you are opening a window, then you make the file alias the} { same as the dir alias } myErr := NewAlias(nil, theFileToOpen, FileAlias); interactErr := AEInteractWithUser(kAEDefaultTimeout, nil, nil); if (interactErr = noErr) then myErr := SetFrontProcess(process); { Create the file list } if (myErr = noErr) then begin myErr := AECreateList(nil, 0, false, fileList); { create the folder descriptor } HLock(Handle(DirAlias)); myErr := AECreateDesc(typeAlias, Ptr(DirAlias^), GetHandleSize(Handle(DirAlias)), aeDirDesc); HUnlock(Handle(DirAlias)); myErr := AEPutParamDesc(aeEvent, keyDirectObject, aeDirDesc); if (myErr = noErr) then begin { done with the desc, kill it } myErr := AEDisposeDesc(aeDirDesc); { create the file descriptor and add to aliasList } HLock(Handle(FileAlias)); myErr := AECreateDesc(typeAlias, Ptr(FileAlias^), GetHandleSize(Handle(FileAlias)), listElem); HLock(Handle(FileAlias)); myErr := AEPutDesc(fileList, 0, listElem); end; if (myErr = noErr) then begin myErr := AEDisposeDesc(listElem); { Add the file alias list to the event } myErr := AEPutParamDesc(aeEvent, aeSelectionKeyword, fileList); myErr := AEDisposeDesc(fileList); if (myErr = noErr) then myErr := AESend(aeEvent, aeReply, kAENoReply + kAEAlwaysInteract + kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, nil, nil); end; end; myErr := AEDisposeDesc(aeEvent); end; end; end; if (DirAlias <> nil) then DisposHandle(Handle(DirAlias)); if (FileAlias <> nil) then DisposHandle(Handle(FileAlias)); OpenSelection := myErr; end; end. --------------------------- From: timper@iat.holonet.net (Tim Perkis) Subject: EditText items larger than 255 bytes? Date: 9 Sep 92 08:33:35 GMT Organization: HoloNet (BBS: 510-704-1058) Is there a reasonable way to access an editText item in a Modal dialog having more than 255 Characters? I know that a Dialog Record has a normal TErecord to hold the text: the bottleneck limiting edit text dialog items to 255 seems to be the GetDItem arguments, which are Str255. Even better, does anyone know of a CDEF or hunk of code available to put a scrollable edit text in a dialog user or control item? Respond by email or posting here. timper@holonet.net +++++++++++++++++++++++++++ From: amanda@intercon.com (Amanda Walker) Date: 9 Sep 92 21:59:09 GMT Organization: InterCon Systems Corporation, Herndon, VA timper@iat.holonet.net (Tim Perkis) writes: > Is there a reasonable way to access an editText item in a Modal > dialog having more than 255 Characters? I know that a Dialog > Record has a normal TErecord to hold the text: the bottleneck limiting > edit text dialog items to 255 seems to be the GetDItem arguments, > which are Str255. Close. TextEdit is limited to 32K, which puts a hard limit on the size of an editable text item. The key to getting more than 255 characters, however, is simply to avoid using GetIText & SetIText, but to use GetDItem and SetDItem directly, copying things into and out of thew handle yourself. Amanda Walker <amanda@intercon.com> - -- "I wouldn't be surprised if the architecture of Intel's microprocessors were eventually linked to the eventual fall of mankind." --Steve Gibson +++++++++++++++++++++++++++ From: resnick@cogsci.uiuc.edu (Pete Resnick) Date: 10 Sep 92 21:07:25 GMT Organization: University of Illinois at Urbana bpb9204@tamsun.tamu.edu (Brent) writes: >amanda@intercon.com (Amanda Walker) writes: >| >|Close. TextEdit is limited to 32K, which puts a hard limit on the size of an >|editable text item. The key to getting more than 255 characters, however, is >|simply to avoid using GetIText & SetIText, but to use GetDItem and SetDItem >|directly, copying things into and out of thew handle yourself. You can't do this! >If you have a modal dialog box with 4 text areas, that's automatically >128k of RAM sucked up for 4x255 = 1020 bytes for actualy data (retrieved or >set via the SetIText/GetIText calls. If you used the MacOS calls to >the letter, that's a 1020/128k percent utilization... pretty awful. No, no, no. RTFM folks. IM I-408: Note: Actually, a single edit record is shared by all editText items; ... So, first of all, you can't look in the TextEdit record to see what's in it because you won't know where in that record the field you want is stored. Second of all, the hText field of the TextEdit record is a handle to the text which changes size as you add and delete text. It is not a static 32K of data. pr - -- Pete Resnick (...so what is a mojo, and why would one be rising?) Graduate assistant - Philosophy Department, Gregory Hall, UIUC System manager - Cognitive Science Group, Beckman Institute, UIUC Internet: resnick@cogsci.uiuc.edu +++++++++++++++++++++++++++ From: stepan@natinst.com (Stepan Riha) Date: 10 Sep 92 21:20:27 GMT Organization: National Instruments, Austin, TX In article <1992Sep10.200136.18513@tamsun.tamu.edu> bpb9204@tamsun.tamu.edu (Brent) writes: [how to get more than 255 chars out of a dialog text item deleted] >Now I have a question. When you make a new TERecord, it is 32k in size, >right? > >If you have a modal dialog box with 4 text areas, that's automatically >128k of RAM sucked up for 4x255 = 1020 bytes for actualy data (retrieved or >set via the SetIText/GetIText calls. If you used the MacOS calls to >the letter, that's a 1020/128k percent utilization... pretty awful. > >Hey, Apple! How about creating a new TextEdit interface so that we can >specify the buffer size to use -- as large or small as we need? > >-Brent When you make a new TERecord you only allocate memory for a TERec which is initially a little over 100 bytes large. The actual text is stored in a handle that grows according to how much text you have. So if your (one-line) text contains 10 characters you'll use about 130 bytes which is less than a Str255. If you actually have 255 characters in your item, you'll need about 43% more memory than if items used Str255; with 10 characters you actually save about 50%. Of course there is the overhead for two handles but that's quite negligible. - -- Stepan Riha -- stepan@natinst.com +++++++++++++++++++++++++++ From: bpb9204@tamsun.tamu.edu (Brent) Date: 10 Sep 92 23:02:40 GMT Organization: Texas A&M Univ., Inc. bpb9204@tamsun.tamu.edu (Brent) writes: | |Now I have a question. When you make a new TERecord, it is 32k in size, |right? I apologize; my brain tries really hard sometimes to take a break. I've used TextEdit stuff before and I don't know why I started thinking like the above... No, the TERec is NOT 32k. - -Brent p.s. please don't email me any more replies to the above blunder. --------------------------- From: korfhage@weston.poly.edu Subject: Adding menus to existing applications (a la Thunder) Date: 12 Sep 92 15:45:40 GMT Organization: Polytechnic University, New York We have a project consisting of a number of programs that interact with a HyperCard stack. All the programs in this project are supposed to have two menus (in addition to their normal menus) - one menu lists the other programs in the project and will launch them, and the other menu sends apple events to the hypercard stack or other programs. The apple event parameters will sometimes include the name of the front window. We have source code for some of the programs, so they are no problem to modify. For other programs, like Excel, we can add macros and external functions to get it to do what we want. However, some programs are simply not customizable to the extent that we need. For these programs, it seems that we need to do something like the Thunder spelling checker, which adds its own menu to the menubar of specific applications. The only problem is that I am not guru enough to know how to do this. Could anyone please help me with explanation and/or pointers to example code? Thanks ever so much! Willard +++++++++++++++++++++++++++ From: haynes@mace.cc.purdue.edu (Carl W. Haynes III) Date: 12 Sep 92 17:58:22 GMT Organization: Purdue University In article <1992Sep12.154540.15753@prism.poly.edu> korfhage@weston.poly.edu writes: > [ asks how to add menus to existing applications ] > I wrote an INIT awhile ago which added a menu to SimAnt (SimAnt Cheater) What I ended up doing was tail-patching _SetMenuBar and _MenuSelect. On SetMenuBar, I would check to see if I was dealing with SimAnt and then Create a new menu and insert it. The patch for _MenuSelect would simply check to see if my menu was selected and respond accordingly. I was only worried about making it work with this one application, I don't know how generalizable this would be to others. If you're only dealing with system 7, I'd suggest looking into how to add icon menus, like the help and application menu, to all applications. (I think they call them Finder Menus). I believe that this years MacHack disk has a couple examples of how to do this. In fact I believe Dylan Talk (available at the usual archives and the MacHack disk) does this also and has source code. carl - -- Carl W. Haynes III Haynes Consulting Services || CWH3@aol.com PO Box 2715 || haynes@mace.cc.purdue.edu W. Lafayette, IN 47906 || hcs@applelink.apple.com - ---------------------------------------------------------------------- Macintosh Programming & Consulting +++++++++++++++++++++++++++ From: leonardr@netcom.com (Leonard Rosenthol) Date: Sat, 12 Sep 92 21:58:35 GMT Organization: Netcom - Online Communication Services (408 241-9760 guest) In article <BuH8LA.Crq@mentor.cc.purdue.edu> haynes@mace.cc.purdue.edu (Carl W. Haynes III) writes: >In article <1992Sep12.154540.15753@prism.poly.edu> korfhage@weston.poly.edu writes: >> [ asks how to add menus to existing applications ] >> > >I wrote an INIT awhile ago which added a menu to SimAnt (SimAnt Cheater) > >What I ended up doing was tail-patching _SetMenuBar and _MenuSelect. > >On SetMenuBar, I would check to see if I was dealing with SimAnt and then >Create a new menu and insert it. The patch for _MenuSelect would simply >check to see if my menu was selected and respond accordingly. >I was only worried about making it work with this one application, I don't >know how generalizable this would be to others. > that's the basic idea, though I would suggest patching _DrawMenuBar instead of _SetMenuBar, since many apps don't call _SetMenuBar. Also, if you are planning any cmdKey equivalents, then you'll need to patch _menuKey as well. >If you're only dealing with system 7, I'd suggest looking into how to >add icon menus, like the help and application menu, to all applications. >(I think they call them Finder Menus). > they are called System Menus, since they are owned by the System... Which brings up a request...PLEASE DO NOT WRITE SYSTEM MENUS!!! They require using a slew of undocumented information as well as NOT being guarenteed to work in the future (at least not the same way). - -- - ----------------------------------------------------------------------------- Leonard Rosenthol Internet: leonardr@netcom.com Director of Advanced Technology AppleLink: MACgician Aladdin Systems, Inc. GEnie: MACgician --------------------------- From: jholt@adobe.com (joe holt) Subject: Game Techniques (was: NON-QUICKDRAW GAMES) Organization: Adobe Systems Inc. Date: Tue, 8 Sep 1992 00:48:21 GMT Whoa! Slow down. All this talk of palettes is way ahead of schedule. First we've got to talk about sync'ing to monitor retrace, opening windows and positioning them on long word boundaries, and--and... Oh, what the heck. PALETTES I must be honest and say I didn't know that much about the Palette Manager until this morning, when I read all of these palette questions and I asked myself, "Yeah, so how does one do that?" You see, my last serious, for profit, game endeavor was several years ago when the word on the street was that the current implementation of the Palette Manager was best avoided. So like any good engineer, I combed the documentation for the lowest-level access to the video card's clut and used it. This information was available in the first edition of "Designing Cards and Drivers for the Macintosh Family (Starring Delta Burke)". Along with the clut calls one could find the calls for changing a monitor's depth without the pesky Monitors control panel. Of course this useful information has been removed from subsequent editions of "Designing Cards" in the interest of public safety (just like Delta Burke). But now everything works, we've been given SetDepth(), and we should use what the system provides. And here's what I came up with this morning: The basic strategy is to create a window "around" whatever direct-to-screen stuff you're going to do, and attach the appropriate palette to it. The window does two useful things for us: it keeps the rest of the system from drawing on top of us--that is, it sorta legitimizes our poking onto the screen (this is what I meant in the first post about screen real estate), and it gives us an easy place to hang a palette that is used whenever the window is frontmost. So let's create the window (this code assumes an 8-bit color display and System 7): // global stuff #include <Palettes.h> static WindowPtr window; static CTabHandle clut; static PaletteHandle palette; .. // inside initialization code Rect bounds; SetRect( &bounds, 50, 50, 512+50, 384+50 ); window = NewCWindow( nil, &bounds, "\pRed", true, noGrowDocProc, (WindowPtr) -1, true, 0 ); SetPort( window ); And stick a palette onto it: clut = (CTabHandle) GetResource( 'clut', 128 ); palette = NewPalette( 32, clut, pmAnimated+pmExplicit, 0 ); SetPalette( window, palette, false ); The 'clut' resource is a ramp from white in index zero to 100% red in index 31. ResEdit has a blend command which makes this easy. Once you get this working, experiment with different values in the clut. The flags to NewPalette() tell the Palette Manager that index zero in the clut resource should be stuck in the video card's clut at index zero, and so on (pmExplicit), and that no one else can use our colors (pmAnimated). As I read Inside Mac 6, the combination of these two makes them the highest priority as the Palette Manager is trying to shoehorn colors into the hardware clut. Basically we're saying that colors 0 through 31 belong to us and nobody else, and here they are. Nyah. To see if all of this works, I draw a ramp of my colors into the window on update events: // In response to update events int c, y; RGBColor black; y = 0; PenSize( 10, 10 ); black.red = black.green = black.blue = 0; TextSize( 9 ); for ( c = 0; c < 32; ++c ) { unsigned char text[100]; PmForeColor( c ); MoveTo( 16, y ); LineTo( 16384, y ); RGBForeColor( &black ); MoveTo( 2, y + 9 ); NumToString( c, text ); DrawString( text ); y += 12; } PenNormal(); Each line in the window lists an index (0 - 31) and shows a bar of that color. Using PmForeColor() says we want the RGB color of that particular index--which, if all this Palette Manager stuff is working, should be the value that's stored into screen memory. Hmmm... what if I just poked those values into memory myself? // Do this on mouse downs, or whenever you want static int c = 0; PixMapHandle pix; Ptr baseAddr, rowBase, p; int rowBytes, x, y; char mmuMode; Rect bounds; unsigned char text[100]; SetRect( &bounds, -32000, -32000, 32000, 32000 ); pix = (**GetMaxDevice( &bounds )).gdPMap; baseAddr = (**pix).baseAddr; rowBytes = (**pix).rowBytes & 0x3FFF; mmuMode = true32b; SwapMMUMode( &mmuMode ); for ( rowBase = baseAddr, y = 30; y; --y ) { for ( p = rowBase, x = 30; x; --x ) *p++ = c; rowBase += rowBytes; } SwapMMUMode( &mmuMode ); TextSize( 12 ); SetRect( &bounds, 485, 0, 16384, 20 ); EraseRect( &bounds ); MoveTo( bounds.left + 5, bounds.bottom - 5 ); NumToString( c, text ); DrawString( text ); c = (c+1) % 32; I wrote this code as my mouse down handler just to give me an easy way to trigger it. It draws a rectangle in the upper left corner of the screen by stuffing values directly into screen memory. The value it stuffs--<c>--is incremented each time this executes and the value is displayed in the upper right corner of our window. - --> I'm using GetMaxDevice() because my main monitor is a 4-bit grayscale two-page display, and my second monitor is the 8-bit one where I want the drawing to take place. Suit to taste. Notice that I SwapMMUMode() around the stuff that actually "draws" into screen memory. As mentioned before, this is necessary only in certain video configurations, but for now it doesn't hurt to be safe, and the alternative is death by poking. So I run this, drag the window over to my 8-bit monitor, and I see a nice red ramp in the window, white at the top and deep red at the bottom. When I click the mouse button a white square appears in the corner of my monitor and the number '0' appears in the window. Each time I click the mouse the number increments and the square gets redder. Wow. What have I just done? In a system-friendly way, I've forced my will upon the video hardware and blasted screen memory. I know that a zero written into screen memory will be color zero from my clut, a one will be color number one, etc. I know this because I TOLD IT. Just to prove that the Palette Manager is really doing it's job, bring up the Monitors control panel and play with the screen depth, moving the app window from frontmost to the back and back and forth. By the way, take a look at the small clut display at the bottom of the control panel. Is the red ramp at the start where you think colors 0 - 31 should be? Wrong! The Monitors control panel shows the entries in reverse order. Go figure. Now of course to really do this right you'd want to put the red square inside the window. Otherwise the system doesn't know we've been drawing and odd visuals result. (For example, put the window's drag bar so that it overlaps the red square and click the mouse button so that the square clobbers part of the drag bar. Then move the window.) This entails finding the screen address of the upper left corner of the *window*, and possibly aligning the window on a long word boundary. But these will have to be subjects of another post, as this one is already pushing the limits of my concentration. /joe +++++++++++++++++++++++++++ From: triantos@acsu.buffalo.edu (Nick B Triantos) Date: 8 Sep 92 04:13:31 GMT Organization: The University at Buffalo First, thanks to Joe Holt and Ben Haller, without whom I still wouldn't have a clue whether it is _really_ possible to do direct screen writes. I do have a question or four which have arisen from building a program based on Joe's words about finding the screen memory, though. On my Mac IIci, with a 640x480 color monitor working through a RasterOps 264 card, 1K of memory is reserved for each row of screen pixels, although the monitor only uses the first 640 bytes of each 1024. So my question is this: Is this a standard behavior for 640x480 video cards? What about for those screens and video cards that produce 800+ pixels horizontally? They should still fit within the 1024 bytes. But how about the 21" monitors that provide some 1200+ pixels horizontally? Do those cards reserve 2K / row, or am I just generalizing something which only my video card is doing? Second, I changed the test part of the program as follows: I did while(!Button()) *myScrnPtr++ = 0xAA and some other things. I make sure that I hit the button before I hit the end of the screen. Now, it seems as though the drawing is still slow (I'd say about 100 lines / sec). Is that slowdown all because of my while(!Button())? I didn't think that that function should be so burdensome to the CPU. Guess that's something I should try out on my own, but it can't hurt to ask. Related to that, I guess it doesn't seem as though my little test program places values in Screen memory at anything close to the rate of, say, moving a 400x300 window. For my example, the system would be moving ~120K bytes, and so it should go relatively quickly, but it seems really fast to me, considering it takes me almost as long to move 1200 bytes. Is the System's window moving code optimized to move more than words at a time? Does the 68000 have something similar to a MOVE.BLK <src>,<dst> instruction? Last question, directed more toward Ben Haller than most others. Does Solarian II use two screens worth of memory and switch between the two for such smooth animation? I understand that you're not animating the whole 640x480 screen at once, but it still does look really nice and smooth. Well, I guess that's all for now. So long, and thanks for all the fish. - -Nick - -- Nick Triantos internet: triantos@acsu.buffalo.edu AOL: Triantos "Scientists tell us that the fastest animal on earth, with a top speed of 120 ft/sec, is a cow that has been dropped out of a helicopter." - Dave Barry +++++++++++++++++++++++++++ From: jholt@adobe.com (joe holt) Organization: Adobe Systems Inc. Date: Tue, 8 Sep 1992 19:13:42 GMT Nick asks some good questions. The biz about 1K per row of 640 bytes is a real shocker, isn't it? *All* of that left over memory! But that's the way it's done--it's easier in hardware to divide by 1K. You'll notice other such wastes as you go to other screen depths/configurations. Most cards do this. RAM is cheap, right? There are three things that slow down your small screen blasting loop: while ( !Button() ) *myScrnPtr++ = 0xAA; #1 is that the Button() call here is a real cycle sink--altho' it's quite snappy compared to, say, CopyDeepMask() with a maskRgn. If you were to profile this code you'd see about 95% going to the Button() call. Here's how it compiles for me: mouse_down +003E 00EFECF4 *BRA.S mouse_down+0044 ; 00EFECFA | 6004 +0040 00EFECF6 MOVE.B #$AA,(A3)+ | 16FC 00AA +0044 00EFECFA CLR.B -(A7) | 4227 +0046 00EFECFC _Button ; A974 | A974 +0048 00EFECFE TST.B (A7)+ | 4A1F +004A 00EFED00 BEQ.S mouse_down+0040 ; 00EFECF6 | 67F4 The branch at the start is only done once to get into the while() loop, so it doesn't count. The button testing code, including the trap dispatch overhead and the actual Button() code in ROM, comes to about 300 cycles. In contrast, the *one* instruction which actually writes to the screen and increments myScrnPtr: +0040 00EFECF6 MOVE.B #$AA,(A3)+ | 16FC 00AA Is all of 12 cycles, or just 4% of the loop. Wow. So get rid of the Button()! You could test the low-mem global MBState yourself, or make it a for loop. Do this and you'll see a blinding increase: while ( *((char *)0x172) < 0 ) // MBState, if you didn't know... *myScrnPtr++ = 0xAA; Get ready to hit that mouse button! #2 is that you're only stuffing bytes at a time. The 680x0 is non-intuitive in that it does *not* take twice as long to write a word into memory, or four times as long to write a long. Words are just as fast as bytes. Let me repeat: whenever you store a byte, you can store a word just as fast. Gee, that makes sense, doesn't it? Whatever the reason for this quantum leap, we game programmers must take advantage of it. But wait. Long words are faster than two words, so that's what you've got to use. Change the loop above to: long *longPtr = (long *) myScrnPtr; while ( *((char *)0x172) < 0 ) *longPtr++ = 0xAAAAAAAA; Now you're talking fast. We're also getting into that gray area where it really would be better just to code the dang thing in assembly. For example, the compiler is likely to code the 0xAAAAAAAA as an immediate operand, but if it's put into a data register the speed almost doubles. This might do it: long *longPtr = (long *) myScrnPtr; long color = 0xAAAAAAAA; while ( *((char *)0x172) < 0 ) *longPtr++ = color; Which in assembly looks like this: mouse_down +0042 00ECA5F8 *MOVEA.L A2,A4 | 284A +0044 00ECA5FA MOVE.L #$AAAAAAAA,D7 | 2E3C AAAA AAAA +004A 00ECA600 BRA.S mouse_down+004E ; 00ECA604 | 6002 +004C 00ECA602 MOVE.L D7,(A4)+ | 28C7 +004E 00ECA604 TST.B MBState | 4A38 0172 +0052 00ECA608 BLT.S mouse_down+004C ; 00ECA602 | 66F8 The first three instructions are only done once, so they don't count. The loop itself-- the last three instructions--is just 34 cycles. 40x faster than the original version! #3 is nit-picking, but it will really speed things up, too. It's pretty silly to test for a mouse down between *every* long. Do it between every ten or so: while ( *((char *)0x172) < 0 ) { *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; } The previous version required 34 cycles per long (12 for the write, 22 for the button test), or 340 cycles for ten longs. Here, however, the time for ten longs has been cut down to 142 cycles (120 for the writes, 22 for the button test). Let's go to the big board and see what our totals are so far: 90x (90x!) faster than the original Button()/ single-byte loop. Jeez, you'd think we could just keep going and eventually fill the entire screen in just a few cycles, right? Well, no. We've sorta reached the end of the speed-up line. These three tricks are at the heart of every fast memory operation: 1. find the fastest instruction that moves the most bytes, 2. keep everything in registers, and 3. unroll the loops. Oops. There is a #4. #4. Only write to what you need to write to. This goes back to the observation at the beginning of the message: there're 384 bytes per scan line (in Nick's configuration) that are unseen (does this remind anyone of the old Apple II text screen holes?). So why write to them?! Change the loops to for loops: one for 640/4 = 160 longs per scan line, and one for 480 scan lines. See how fast you can fill the screen. Change the color on each fill and let it rip. You'll be amazed! Here's my final version of the thing: register int x, y; register long color = 0; char mmuMode; mmuMode = true32b; SwapMMUMode( &mmuMode ); while ( *((char *)0x172) < 0 ) { register long *rowBase = (long *) baseAddr; for ( y = 480; y; --y ) { register long *longPtr = rowBase; for ( x = 640/4/10; x; --x ) { *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; *longPtr++ = color; } rowBase = (long *) ((char *)rowBase + rowBytes); } color += 0x01010101; // next color } SwapMMUMode( &mmuMode ); Have fun! /joe +++++++++++++++++++++++++++ From: d88-jwa@musta.nada.kth.se (Jon W{tte) Date: 9 Sep 92 12:40:48 GMT Organization: Royal Institute of Technology, Stockholm, Sweden In article <1992Sep8.191342.15509@adobe.com> jholt@adobe.com (joe holt) writes: [ Lots deleted ] 1. find the fastest instruction that moves the most bytes, 2. keep everything in registers, and 3. unroll the loops. Oops. There is a #4. #4. Only write to what you need to write to. This goes back to the That's not only faster, it avoids crashes on some cards. You are supposed to NOT write anywhere the screen doesn't show your pixels. However, your timings are off. If your mac has color, it's a 030 mac, where the instructions take much fewer cycles, and the relation is different. Not to mention the 68040, which uses one or two cycles for manu instructions ! You could special-case your code, so if it's on a 040 and the moves are cache line aligned, you do a MOVE16 to move four longs at a time. Note, this is another important thing: alignment. Always make sure that reads/writes of size N is integer divisible by N (i.e. address modulo N equals 0) - -- Jon W{tte, h+@nada.kth.se, Sweden, Phone +46-8-107069 Help eradicate FIDO-Net <-> Usenet gateways in our time! +++++++++++++++++++++++++++ From: robichau@lambda.msfc.nasa.gov (Paul Robichaux) Date: 9 Sep 92 15:31:23 GMT Organization: New Technology, Inc. In <D88-JWA.92Sep9134048@musta.nada.kth.se> d88-jwa@musta.nada.kth.se (Jon W{tte) writes: [ deletia - 4 things to do for fast drawing ] >However, your timings are off. If your mac has color, it's >a 030 mac Nope. The original Mac II and the original Mac LC were both '020 machines. - -- Paul Robichaux, KD4JZG | I shouldn't bitch / I shouldn't cry robichau@lambda.msfc.nasa.gov | I'd start a revolution but I don't have time. +++++++++++++++++++++++++++ From: d88-jwa@cyklop.nada.kth.se (Jon W{tte) Organization: Royal Institute of Technology, Stockholm, Sweden Date: Wed, 9 Sep 1992 18:28:44 GMT > robichau@lambda.msfc.nasa.gov (Paul Robichaux) writes: >However, your timings are off. If your mac has color, it's >a 030 mac Nope. The original Mac II and the original Mac LC were both '020 machines. Yeah, right, but! I think the timing case should be made for a 030, since that's the mainstream processor (and the 040 is fast enough anuyway :-) We are already discounting all people running Stepping Out, Radius Pivots and SCSI screen boxes, since we don't use the ShieldCursor/ ShowCursor signalling mechanism. - -- Jon W{tte, h+@nada.kth.se, Sweden, Phone +46-8-107069 Help eradicate FIDO-Net <-> Usenet gateways in our time! +++++++++++++++++++++++++++ From: triantos@acsu.buffalo.edu (Nick B Triantos) Date: 10 Sep 92 14:27:51 GMT Organization: The University at Buffalo Hi, peoples. I've been basing some test programs on the code that Joe Holt has provided over the past few days (btw, thanks, Joe!). Anyhow, I'm having a problem as follows: I want to draw blocks of color on my monitor, each of size 40x30, for a total of 256, in a 16x16 grid. Each one will be an increment in color from the previous. I find the base screen address using Joe's method, and then the code at the bottom to try to do what I want. My two problems are: 1. After my first time cycling through my i for-loop, x_offset is being set to 1, even though x is still 1. 2. More importantly, from the THINK debugger, offset + base_address (an extern Ptr) + i * 1024 moves me to the correct scan line, but I get an error compiling with longPtr assigned to this equation because of a type conflict. But if I type cast base_address to (long *) (as done in the code, see PROBLEM LINE below), it changes the value of longPtr from being incremented 0x0400 to being incremented by 0x1000. Needless to say, my program therefore doesn't work. I though I was only moderately rusty with my C. What am I doing wrong? Many thanks, - -Nick code follows ... void DirectDrawTest() { register int x,y ; register long int x_offset = 0, y_offset = 0 ; register long int i ; register long int color = 0 ; register long int *longPtr ; register long int offset ; char old_MMUMode ; /* Switch to 32-bit addressing, for 32-bit video cards */ if ( (myErr == noErr) && (This_mac.hasColorQD) ) { old_MMUMode = true32b ; SwapMMUMode( &old_MMUMode ) ; } /* No need to have the cursor erase my masterpiece... */ HideCursor() ; /* Place 16 40x30 squares in each of 16 rows, inc.'ing colors as we go */ for ( y = 0 ; y < 16 ; y++ ) /* 16 vert. boxes */ { for ( x = 0 ; x < 16 ; x++ ) /* 16 horiz. boxes */ { x_offset = 40 * x ; /* 40 horiz. pixels / color box */ offset = x_offset + y_offset ; for ( i = 0 ; i < 30 ; i++ ) /* 30 lines of 40 pix. ea per box */ { PROBLEM LINE: longPtr = offset + (long *)base_address + i * 1024 ; /* 1024=bytes/scan line */ *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; *longPtr++ = color ; } color += 0x01010101 ; } y_offset += 30 * 1024 ; /* move down 30 pix. after each row of boxes */ } /* Go back to old video mode */ if ( This_mac.hasColorQD ) SwapMMUMode( &old_MMUMode ) ; while (!Button()) ; /* pause until the mouse button is hit. */ ShowCursor() ; if (Button()) while ( Button() ) ; /* Wait for button release */ } - -- Nick Triantos internet: triantos@acsu.buffalo.edu AOL: Triantos "Scientists tell us that the fastest animal on earth, with a top speed of 120 ft/sec, is a cow that has been dropped out of a helicopter." - Dave Barry +++++++++++++++++++++++++++ From: chris@benton.prepress.com (christopher m. knox) Date: 10 Sep 92 17:05:49 GMT Organization: Pre-Press Technologies, Inc. Subject: Re: Game Techniques (was: NON-QUICKDRAW GAMES) From: Nick B Triantos, triantos@acsu.buffalo.edu In article <BuD6qK.7HM@acsu.buffalo.edu> Nick B Triantos, triantos@acsu.buffalo.edu writes: >2. More importantly, from the THINK debugger, > offset + base_address (an extern Ptr) + i * 1024 > moves me to the correct scan line, but I get an error compiling with > longPtr assigned to this equation because of a type conflict. But if I > type cast base_address to (long *) (as done in the code, see PROBLEM LINE > below), it changes the value of longPtr from being incremented 0x0400 to > being incremented by 0x1000. > >PROBLEM LINE: longPtr = offset + (long *)base_address + i * 1024 ; > /* 1024=bytes/scan line */ Greetings... I've grappled with the same problem a few times. In C, if you increment a pointer, it gets incremented by the size of the base type e.g.: char *pc; long *pl; ++pc; /* gets incremented by 1 byte, since sizeof(char) is 1 byte */ ++pl; /* gets incremented by 4 bytes, since sizeof(long) is 4 bytes */ this is convenient most of the time but also easy to forget. To fix your problem line, change it to: longPtr = (long *) ((long) offset + (long) base_address + (long) i * 1024L); this performs the calculation as arithmetic rather than pointer arithmetic (all the casts are not necessary but at least you know exactly what's going on). Hope this helps. chris knox "Omne animal triste est post (or without) coitum" --------------------------- End of C.S.M.P. Digest **********************